package net.jgrm.redisadvanced.service.impl;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import net.jgrm.redisadvanced.config.RedisAdvancedProperties;
import net.jgrm.redisadvanced.service.RedisHashService;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * Redis Hash类型操作服务实现类
 */
@Slf4j
//@Service
public class RedisHashServiceImpl implements RedisHashService {

    //    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    //    @Autowired
    private RedisAdvancedProperties redisAdvancedProperties;

    //    @Autowired
    private ObjectMapper objectMapper;

    public RedisHashServiceImpl(RedisTemplate<String, Object> redisTemplate, RedisAdvancedProperties redisAdvancedProperties, ObjectMapper objectMapper) {
        this.redisTemplate = redisTemplate;
        this.redisAdvancedProperties = redisAdvancedProperties;
        this.objectMapper = objectMapper;
    }

    /**
     * 添加前缀到键名
     */
    private String addPrefix(String key) {
        return redisAdvancedProperties.getKeyPrefix() + key;
    }

    @Override
    public void put(String key, String hashKey, Object value) {
        try {
            redisTemplate.opsForHash().put(addPrefix(key), hashKey, value);
            log.debug("Hash put successful: key={}, hashKey={}", key, hashKey);
        } catch (Exception e) {
            log.error("Hash put error: key={}, hashKey={}, error={}", key, hashKey, e.getMessage(), e);
            throw new RuntimeException("Redis hash put operation failed", e);
        }
    }

    @Override
    public void putAll(String key, Map<String, Object> map) {
        try {
            redisTemplate.opsForHash().putAll(addPrefix(key), map);
            log.debug("Hash putAll successful: key={}", key);
        } catch (Exception e) {
            log.error("Hash putAll error: key={}, error={}", key, e.getMessage(), e);
            throw new RuntimeException("Redis hash putAll operation failed", e);
        }
    }

    @Override
    public <T> T get(String key, String hashKey, Class<T> clazz) {
        try {
            Object value = redisTemplate.opsForHash().get(addPrefix(key), hashKey);
            log.debug("Hash get successful: key={}, hashKey={}", key, hashKey);
            return convertValue(value, clazz);
        } catch (Exception e) {
            log.error("Hash get error: key={}, hashKey={}, error={}", key, hashKey, e.getMessage(), e);
            throw new RuntimeException("Redis hash get operation failed", e);
        }
    }

    @Override
    public <T> Map<String, T> getAll(String key, Class<T> clazz) {
        try {
            Map<Object, Object> entries = redisTemplate.opsForHash().entries(addPrefix(key));
            log.debug("Hash getAll successful: key={}", key);
            return convertMap(entries, clazz);
        } catch (Exception e) {
            log.error("Hash getAll error: key={}, error={}", key, e.getMessage(), e);
            throw new RuntimeException("Redis hash getAll operation failed", e);
        }
    }

    @Override
    public <T> Map<String, T> multiGet(String key, String[] hashKeys, Class<T> clazz) {
        try {
            List<Object> hashKeyList = Arrays.asList(hashKeys);
            List<Object> values = redisTemplate.opsForHash().multiGet(addPrefix(key), hashKeyList);
            log.debug("Hash multiGet successful: key={}", key);

            Map<String, T> result = new HashMap<>(hashKeys.length);
            for (int i = 0; i < hashKeys.length; i++) {
                Object value = values.get(i);
                if (value != null) {
                    result.put(hashKeys[i], convertValue(value, clazz));
                }
            }
            return result;
        } catch (Exception e) {
            log.error("Hash multiGet error: key={}, error={}", key, e.getMessage(), e);
            throw new RuntimeException("Redis hash multiGet operation failed", e);
        }
    }

    @Override
    public Boolean hasKey(String key, String hashKey) {
        try {
            Boolean result = redisTemplate.opsForHash().hasKey(addPrefix(key), hashKey);
            log.debug("Hash hasKey successful: key={}, hashKey={}", key, hashKey);
            return result;
        } catch (Exception e) {
            log.error("Hash hasKey error: key={}, hashKey={}, error={}", key, hashKey, e.getMessage(), e);
            throw new RuntimeException("Redis hash hasKey operation failed", e);
        }
    }

    @Override
    public Set<String> keys(String key) {
        try {
            Set<Object> keys = redisTemplate.opsForHash().keys(addPrefix(key));
            log.debug("Hash keys successful: key={}", key);

            Set<String> result = new HashSet<>();
            for (Object hashKey : keys) {
                result.add(hashKey.toString());
            }
            return result;
        } catch (Exception e) {
            log.error("Hash keys error: key={}, error={}", key, e.getMessage(), e);
            throw new RuntimeException("Redis hash keys operation failed", e);
        }
    }

    @Override
    public <T> java.util.List<T> values(String key, Class<T> clazz) {
        try {
            List<Object> values = redisTemplate.opsForHash().values(addPrefix(key));
            log.debug("Hash values successful: key={}", key);
            return convertList(values, clazz);
        } catch (Exception e) {
            log.error("Hash values error: key={}, error={}", key, e.getMessage(), e);
            throw new RuntimeException("Redis hash values operation failed", e);
        }
    }

    @Override
    public Long size(String key) {
        try {
            Long result = redisTemplate.opsForHash().size(addPrefix(key));
            log.debug("Hash size successful: key={}", key);
            return result;
        } catch (Exception e) {
            log.error("Hash size error: key={}, error={}", key, e.getMessage(), e);
            throw new RuntimeException("Redis hash size operation failed", e);
        }
    }

    @Override
    public Long delete(String key, Object... hashKeys) {
        try {
            Long result = redisTemplate.opsForHash().delete(addPrefix(key), hashKeys);
            log.debug("Hash delete successful: key={}", key);
            return result;
        } catch (Exception e) {
            log.error("Hash delete error: key={}, error={}", key, e.getMessage(), e);
            throw new RuntimeException("Redis hash delete operation failed", e);
        }
    }

    @Override
    public Long increment(String key, String hashKey, long delta) {
        try {
            Long result = redisTemplate.opsForHash().increment(addPrefix(key), hashKey, delta);
            log.debug("Hash increment successful: key={}, hashKey={}, delta={}", key, hashKey, delta);
            return result;
        } catch (Exception e) {
            log.error("Hash increment error: key={}, hashKey={}, error={}", key, hashKey, e.getMessage(), e);
            throw new RuntimeException("Redis hash increment operation failed", e);
        }
    }

    @Override
    public Double increment(String key, String hashKey, double delta) {
        try {
            Double result = redisTemplate.opsForHash().increment(addPrefix(key), hashKey, delta);
            log.debug("Hash increment double successful: key={}, hashKey={}, delta={}", key, hashKey, delta);
            return result;
        } catch (Exception e) {
            log.error("Hash increment double error: key={}, hashKey={}, error={}", key, hashKey, e.getMessage(), e);
            throw new RuntimeException("Redis hash increment double operation failed", e);
        }
    }

    @Override
    public Boolean putIfAbsent(String key, String hashKey, Object value) {
        try {
            Boolean result = redisTemplate.opsForHash().putIfAbsent(addPrefix(key), hashKey, value);
            log.debug("Hash putIfAbsent successful: key={}, hashKey={}", key, hashKey);
            return result;
        } catch (Exception e) {
            log.error("Hash putIfAbsent error: key={}, hashKey={}, error={}", key, hashKey, e.getMessage(), e);
            throw new RuntimeException("Redis hash putIfAbsent operation failed", e);
        }
    }

    @Override
    public Boolean expire(String key, long expireTime, TimeUnit timeUnit) {
        try {
            Boolean result = redisTemplate.expire(addPrefix(key), expireTime, timeUnit);
            log.debug("Hash expire successful: key={}, expireTime={}, timeUnit={}", key, expireTime, timeUnit);
            return result;
        } catch (Exception e) {
            log.error("Hash expire error: key={}, error={}", key, e.getMessage(), e);
            throw new RuntimeException("Redis hash expire operation failed", e);
        }
    }

    /**
     * 将单个对象转换为指定类型
     */
    private <T> T convertValue(Object value, Class<T> clazz) {
        if (value == null) {
            return null;
        }
        if (clazz.isInstance(value)) {
            return clazz.cast(value);
        } else {
            return objectMapper.convertValue(value, clazz);
        }
    }

    /**
     * 将对象列表转换为指定类型的列表
     */
    private <T> List<T> convertList(List<Object> values, Class<T> clazz) {
        List<T> result = new ArrayList<>(values.size());
        for (Object value : values) {
            result.add(convertValue(value, clazz));
        }
        return result;
    }

    /**
     * 将对象Map转换为指定类型的Map
     */
    private <T> Map<String, T> convertMap(Map<Object, Object> map, Class<T> clazz) {
        Map<String, T> result = new HashMap<>(map.size());
        for (Map.Entry<Object, Object> entry : map.entrySet()) {
            String key = entry.getKey().toString();
            T value = convertValue(entry.getValue(), clazz);
            result.put(key, value);
        }
        return result;
    }
}